#include "qtplatform.h"
#include <renderobject.h>
using namespace NITK;

static QPointF fromPoint(const Point &point)
{
    return QPointF(point.x(), point.y());
}

static QRectF fromRectangle(const Rectangle &rectangle)
{
    return QRectF(rectangle.position().x(),
                  rectangle.position().y(),
                  rectangle.size().width(),
                  rectangle.size().height());
}

static Point toPoint(const QPointF &point)
{
    return Point(point.x(), point.y());
}

static Rectangle toRectangle(const QRectF &rectangle)
{
    return Rectangle(toPoint(rectangle.topLeft()), Size(rectangle.width(), rectangle.height()));
}


QtPlatformData *QtPlatformData::inst = 0;
QPainter *QtPainter::inst = 0;

Image::Image()
{
    data = new QtImageData;
}

Image::~Image()
{
    delete data;
}

bool Image::load(const std::string &path)
{
    return static_cast<QtImageData*>(data)->pixmap.load(QString::fromStdString(path));
}

void Image::render(const Rectangle &rectangle)
{
    QtPainter::current()->drawPixmap(fromRectangle(rectangle).toRect(),
                                     static_cast<QtImageData*>(data)->pixmap);
}

void Platform::init(int &argc, char **argv)
{
    QtPlatformData *p = new QtPlatformData;
    data = p;
    p->inst = p;
    p->application = new QApplication(argc, argv);
    p->view = new GraphicsView(this);
    p->view->setScene(p->scene = new QGraphicsScene(p->view));
    p->view->show();
}

int Platform::exec()
{
    return static_cast<QtPlatformData*>(data)->application->exec();
}

Platform::~Platform()
{
    QtPlatformData *p = static_cast<QtPlatformData*>(data);
    delete p->view;
    //app->quit();
    delete p->application;
    delete data;
}


void Platform::update(const Rectangle &rectangle)
{
    static_cast<QtPlatformData*>(data)->scene->update(fromRectangle(rectangle));
}

Size Platform::windowSize() const
{
    return toRectangle(static_cast<QtPlatformData*>(data)->scene->sceneRect()).size();
}

void Platform::update(RenderObject *object)
{
    QtRenderObjectData *data = static_cast<QtRenderObjectData*>(object->data);
    data->graphicsItem->update();
}

int Platform::startTimer(int interval, TimerCallback func)
{
    assert(func);
    assert(interval >= 0);
    QtPlatformData *p = static_cast<QtPlatformData*>(data);
    const int ret = p->startTimer(interval);
    QtPlatformData::Timer &t = p->timers[ret];
    t.interval = interval;
    t.callback = func;
    qDebug() << interval << ret << p->timers.keys() << this;
    return ret;
}

bool Platform::stopTimer(int id)
{
    qDebug() << static_cast<QtPlatformData*>(data)->timers.keys() << this;
    if (static_cast<QtPlatformData*>(data)->timers.remove(id) > 0) {
        static_cast<QtPlatformData*>(data)->killTimer(id);
        return true;
    }
    return false;
}

std::list<int> Platform::timers() const
{
    return static_cast<QtPlatformData*>(data)->timers.keys().toStdList();
}

int Platform::timerInterval(int timerId) const
{
    return static_cast<QtPlatformData*>(data)->timers.value(timerId).interval;
}


QtRenderObject::QtRenderObject(QGraphicsScene *scene, RenderObject *object)
    : QGraphicsRectItem(0, scene), renderObject(object)
{
}

void QtRenderObject::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *)
{
    if (renderObject->image()) {
        QtPainter::setCurrent(painter);
        renderObject->image()->render(toRectangle(option->rect));
    } else if (renderObject->background()) {
        int r, g, b, a;
        Color::toRgba(renderObject->background(), r, g, b, a);
        painter->fillRect(option->rect, QColor(r, g, b, a));
    }
    const std::string &t = renderObject->text();
    if (t.length() > 0) {
        const unsigned int align = renderObject->alignment();
        uint qtalign = 0;
        if (align & RenderObject::AlignLeft) {
            qtalign |= Qt::AlignLeft;
        } else if (align & RenderObject::AlignRight) {
            qtalign |= Qt::AlignRight;
        }

        if (align & RenderObject::AlignTop) {
            qtalign |= Qt::AlignTop;
        } else if (align & RenderObject::AlignBottom) {
            qtalign |= Qt::AlignBottom;
        }

        // ### margin?
        painter->save();

        int r, g, b, a;
        Color::toRgba(renderObject->foreground(), r, g, b, a);
        painter->setPen(QColor(r, g, b, a));
        painter->drawText(option->rect, qtalign, QString::fromStdString(t));
        painter->restore();
    }
}

void RenderObject::init(RenderObject *object, int type)
{
    data = new QtRenderObjectData(object, type);
    geometryChanged();
}

void RenderObject::geometryChanged()
{
    QtRenderObject *item = static_cast<QtRenderObjectData*>(data)->graphicsItem;
    item->setRect(fromRectangle(geometry()));
    item->setZValue(zValue());
    qobject_cast<GraphicsView*>(item->scene()->views().first())->itemGeometryChanged(item);
}

void GraphicsView::itemGeometryChanged(QtRenderObject *object) // ### do I even need this stuff?
{
 //    enum { NeedBottom = 0x1, NeedRight = 0x2 };
//     uint status = 0;
//     if (object != bottom) {
//         const double origBottom = bottom->sceneBoundingRect().bottom();
//         const double newBottom = object->sceneBoundingRect().bottom();
//         if (newBottom > origBottom) {
//             bottom = object;
//         }
//     } else {
//         status |= NeedBottom;
//     }

//     if (object != right) {
//         const double origRight = right->sceneBoundingRect().right();
//         const double newRight = object->sceneBoundingRect().right();
//         if (newRight > origRight) {
//             right = object;
//         }
//     } else {
//         status |= NeedRight;
//     }

//     if (status) {
//         double b =
//         foreach(QGraphicsItem *item, scene()->items()) {
//             const QRectF r = item->sceneBoundingRect();

//             QtRenderObject *renderObject = static_cast<QtRenderObject*>(
//         }
//     }
}

bool GraphicsView::viewportEvent(QEvent *e)
{
    switch (e->type()) {
    case QEvent::MouseMove:
    case QEvent::MouseButtonPress:
    case QEvent::MouseButtonRelease: {
        PositionEvent p(e->type() == QEvent::MouseButtonPress ? Event::Press :
                        (e->type() == QEvent::MouseButtonRelease ? Event::Release
                         : Event::Drag),
                        toPoint(mapToScene(static_cast<QMouseEvent*>(e)->pos())));
        platform->sendEvent(&p);
        break; }
    default:
        break;
    }
    return QGraphicsView::viewportEvent(e);
}

bool GraphicsView::event(QEvent *e)
{
    switch (e->type()) {
#ifdef Q_WS_MAC
    case QEvent::Show:
        activateWindow();
        raise();
        break;
#endif
    case QEvent::Resize: {
        GeometryEvent r(Event::WindowGeometryChange, toRectangle(viewport()->geometry()));
        platform->sendEvent(&r);
        break; }
    default:
        break;
    }
    return QGraphicsView::event(e); // ### should I always pass the events on?
}
